package sceneInfo;

import java.io.Serializable;
import java.util.Vector;

/*
 * a framework class covering all the fuzzy logic stuff
 */
public class FuzzyFramework implements Serializable {
	
	public static final long serialVersionUID = 2L;
	
	public float[] alphameters; //parameters of angle membership fcts
	public float[] distameters; //parameters of dist membership fcts
	
	/**number of different "bins" for angle; must equal alphameters.length*/
	public int alphanumber; 
	/**number of different "bins" for distance; must equal distameters.length*/
	public int distnumber; // number of different classes for distance; must equal distameters.length
	
	
	public boolean isfuzzy; // defines whether we use just histograms or also fuzzy logic (default is fuzzy)
	
/**Constructor to make a fuzzy framework from float arrays
 * 
 * @param aparams The parameters for the angle segmentation
 * @param distparams The parameters for the distance dimension segmentation
 */	
public FuzzyFramework(float [] aparams, float[] distparams, boolean fuzz){
	
	alphameters = aparams.clone() ; //parameters of angle membership fcts
	distameters = distparams.clone(); //parameters of dist membership fcts
	alphanumber= alphameters.length ; //number of different "classes" for angle; must equal alphameters.length
	distnumber= distameters.length ; // number of different classes for distance; must equal distameters.length
	
	isfuzzy= fuzz;
	
	
	//output parameters
	System.out.println("angle params"); 
	for (int i =0; i<alphanumber; i++){
		System.out.print(alphameters[i]+ " ");	
	}
	System.out.println();
	System.out.println("distance params"); 
	for (int i =0; i<distnumber; i++){
		System.out.print(distameters[i]+ " ");
	}
	//done
}

public FuzzyFramework(float [] aparams, float[] distparams){
	
	this(aparams, distparams, true);	//calls other constructor
}

/**Constructor to make a fuzzy framework from Vector<Float> objects
 * 
 * @param aparams The parameters for the angle segmentation
 * @param distparams The parameters for the distance dimension segmentation
 */
public FuzzyFramework(Vector<Float> aparams, Vector<Float> distparams){
	
	alphanumber = aparams.size();
	alphameters = new float [alphanumber];    //aparams.clone() ; //parameters of angle membership fcts
	//converting the vector into a float array. perhaps not the smartest thing...
	int i =0;
	for (Float ff : aparams){
		alphameters[i++]= ff.floatValue();	
	}

	distnumber = distparams.size();
	distameters = new float [distnumber];   
	//converting the vector into a float array. perhaps not the smartest thing...
	i =0;
	for (Float ff : distparams){
		distameters[i++]= ff.floatValue();	
	}
	
	//output parameters
	System.out.println("angle params"); 
	for (i =0; i<alphanumber; i++){
		System.out.print(alphameters[i]+ " ");	
	}
	System.out.println();
	System.out.println("distance params"); 
	for (i =0; i<distnumber; i++){
		System.out.print(distameters[i]+ " ");
	}
	//done
}	


	/** returns membership of object objectInfo in bin number 'bin'
	 * 
	 * @return membership is an int from 0 to 100, a percentage membership
	 * 
	 * @param bin which bin of the histogram : angle bin is bin mod distnumber, distance bin is bin / alphanumber
	 * @param obj the ObjectInfo to be placed on the grid
	 * */
	public int membership(int bin, ObjectInfo obj) {
		if(isfuzzy)
			return fuzzyMembership(bin, obj.getDirection(), obj.getDistance()); //use fuzzy discretization
		else
			return crispMembership(bin, obj.getDirection(), obj.getDistance()); // use discretization without fuzzy logic
	}
	
	/** returns membership of object at polar coordinates alpha, dist 
	 * in bin number 'bin'
	 * angle bin is bin mod distnumber,
	 * distance bin is bin / alphanumber
	 * membership is an int from 0 to 100, a percentage membership
	 * this one deals with the fuzzy case
	 * */
	private int fuzzyMembership(int bin, float alpha, float dist) {
	
		int anglebin = bin % alphanumber;
		int distbin = bin / alphanumber; //+1? or do arrays start at zero?
		return fuzzyMembership(anglebin, distbin,alpha, dist);
	}

	/** returns membership of object at polar coordinates alpha, dist 
	 * in bin number  identified by angle /distance
	 * membership is an int from 0 to 100, a percentage membership
	 * this one deals with the fuzzy case
	 * */	
	private int fuzzyMembership(int anglebin, int distbin, float alpha, float dist) {
		
		float acentre = alphameters[anglebin]; //centre of the support interval for the angle bin
		float dcentre = distameters[distbin]; //centre of the support interval for the angle 									
		float angleresult=0; //membership for the angle value
		float distresult=0; //membership for distance value

		//calculate membership of alpha in different angle classes----------------------------

		//get interval boundaries straight away to avoid unnecessary calcuations
		double aleft; //using doubles because later these are compared with other doubles involved in sine / cosine calculations
		if (anglebin>0) 
			aleft = alphameters[anglebin-1];
		else 
			aleft=0; // will not be used, just so the compiler doesnt omcplain the variable isn't initialized
		
		double aright;
		if (anglebin!=alphanumber-1) 
			aright= alphameters[anglebin+1]; // high bound of interval (right side)
		else
			aright=0;//same
		
		float dright;
		if (distbin!=distnumber-1) 
			dright = distameters[distbin+1]; // high bound of interval (right side)
		else
			dright=0;//same
		
		float dleft; 
		if (distbin>0) 
			dleft =  distameters[distbin-1];
		else 
			dleft=0; // will not be used, just so the compiler doesnt omcplain the variable isn't initialized
		
		
		//first isolate case where the considered object is at the extreme edges		
		if (alpha <= alphameters[0] && anglebin==0){
			angleresult=1;
			}
		else if (alpha >= alphameters[alphanumber-1] && anglebin==alphanumber-1) { 
			angleresult=1;		
		}
		//same for distance                ------------------------
		if (dist <= distameters[0] && distbin==0){
			distresult=1;
		}
		else if (dist >= distameters[distnumber-1] && distbin==distnumber-1) { 
			distresult=1;
		}
		
		//case: out of the support interval
		if ((alpha <= aleft)||(alpha >= aright)||(dist >= dright)||(dist <= dleft) ){
			return 0;
		}
		
			
		// ------ now we know that we're inside the support interval ------
		if (angleresult!=1) // we haven't already calculated the angle membership  
		{
			// see which side of the support interval we're on
			// use sine/cosine squared membership function
			if (alpha <= acentre){// case left
				//lower bound of the interval (left side)
				// return zero if the angle is not in the support interval of the class
				double sine;
				sine = Math.sin((Math.PI/2)*(alpha-aleft)/(acentre-aleft)); // sine with right parameters 
				angleresult = (float)(sine*sine); //sine squared

			}
			////in this case we're on the right-hand side of the interval,
			else {
				// here we're in the interval, on the right side => mf = cos2
				double cosine;
				cosine = Math.cos((Math.PI/2)*(alpha-acentre)/(aright-acentre)); // cosine with right parameters 
				angleresult = (float)(cosine*cosine); //sine squared, cast to float

			}
		}// end of angle membership calculation ------------------------

		if(distresult!=1){ //we haven't already got the distance membership from the exception cases above

			// use triangular membership function 

			// see which side of the support interval we're on
			if (dist <= dcentre)// case left-side of the interval because value less than interval centre
				distresult = (dist-dleft)/(dcentre-dleft); // linear membership from 0 to 1 in interval 
			else //in this case we're on the right-hand side of the interval
				distresult = (dright-dist)/(dright-dcentre); // linear membership from 0 to 1 in interval
		}// end of distance membership calculation -----------------------

	// we round the result to get an integer (percentage membership)		
	return Math.round(angleresult*distresult*100);
	}

	/** returns membership of object at polar coordinates alpha, dist 
	 * in bin number 'bin'
	 * angle bin is bin mod distnumber,
	 * distance bin is bin / alphanumber
	 * membership is an int from 0 to 100, a percentage membership
	 * */
	private int crispMembership(int bin, float alpha, float dist) {
	
		int anglebin = bin % alphanumber;
		int distbin = bin / alphanumber; //+1? or do arrays start at zero?
		float acentre = alphameters[anglebin]; //centre of the support interval for the angle bin
		float dcentre = distameters[distbin]; //centre of the support interval for the angle 									
		float angleresult=0; //membership for the angle value
		float distresult=0; //membership for distance value

		//calculate membership of alpha in different angle classes----------------------------
		//first isolate case where the considered object is at the extreme edges
		if (alpha <= alphameters[0] && anglebin==0){
			angleresult=1;
			}
		else if (alpha >= alphameters[alphanumber-1] && anglebin==alphanumber-1) { 
			angleresult=1;
		}
		// else use sine/cosine squared membership function 
		else {
			// see which side of the support interval we're on (aleft and aright may not exist)
			if (alpha <= acentre){// case left
				double aleft = alphameters[anglebin-1]; //lower bound of the interval (left side)
				// return zero if the angle is not in the support interval of the class
				if (alpha <= ((aleft+acentre)/2)) {
					return 0; // no need to continue
					}
				else {// in this case we're on the left-hand side of the interval,
					angleresult=1;
				}
			}
			////in this case we're on the right-hand side of the interval,
			else {
				double aright = alphameters[anglebin+1]; // high bound of interval (right side)
				//case: out of the support interval
				if (alpha >= ((aright+acentre) /2)) {
					return 0; //no need to continue
				}
				else { // here we're in the interval, on the right side => mf = cos2
					angleresult = 1;
				}
			}
		}// end of angle membership calculation ------------------------
		
		//same for distance                ------------------------
		if (dist <= distameters[0] && distbin==0){
			distresult=1;
			}
		else if (dist >= distameters[distnumber-1] && distbin==distnumber-1) { 
			distresult=1;
		}

		// else use triangular membership function 
		else {
			// see which side of the support interval we're on

			if (dist <= dcentre){// case left-side of the interval because value less than interval centre
				
				float dleft = distameters[distbin-1];
				if (dist <= ((dleft+dcentre)/2)) {// return zero if the distance is not in the support interval of the class
					return 0;
					}
				else {// in this case we're on the left-hand side of the interval, ie near the lower bound
					distresult = 1; 
					
				}
			}
			//case right
			else {
				float dright = distameters[distbin+1];
				//case: out of the support interval
				if (dist >= ((dright+dcentre)/2)) {
					return 0;
				}
				else {//in this case we're on the right-hand side of the interval, mf = cos2
					
					distresult = 1;
				}
			}
		}// end of distance membership calculation -----------------------
	
	// we round the result to get an integer (percentage membership)		
	return Math.round(angleresult*distresult*100); // here it will be 1!
	}

	
	/**
	 * get size of the grid that this fuzzy framework creates.
	 * @return
	 */
	public int getGridSize() {
		
		return alphanumber * distnumber;
	}
}
